package com.longtailvideo.plugins.captions {


    import com.longtailvideo.jwplayer.events.*;
    import com.longtailvideo.jwplayer.player.*;
    import com.longtailvideo.jwplayer.plugins.*;
    import com.longtailvideo.jwplayer.utils.*;
    import com.longtailvideo.jwplayer.view.interfaces.*;
    
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;


    /** Plugin for playing closed captions with a video. **/
    public class Captions extends Sprite implements IPlugin {


        /** Embeds of the default graphics. **/
        [Embed(source="../../../../../assets/controlbar.png")]
        private const ControlbarIcon:Class;
        [Embed(source="../../../../../assets/dock.png")]
        private const DockIcon:Class;


        /** Reference to the controlbar/dock button. **/
        private var _button:MovieClip;
        /** Save the last resize dimensions. **/
        private var _dimensions:Array;
        /** List with configuration options. **/
        private var _config:Object = {
            back: false,
            label: undefined,
            file: undefined,
            state: true
        };
        /** Cookie object for storing track prefs. **/
        private var _cookie:SharedObject;
        /** Default style properties. **/
        private var _defaults:Object = {
            color: '#FFFFFF',
            fontFamily: 'Arial,sans-serif',
            fontSize: 15,
            fontStyle: 'normal',
            fontWeight: 'normal',
            leading: 5,
            textAlign: 'center',
            textDecoration: 'none'
        };
        /** Anomator instance that eases the renderer. **/
        private var _easer:Animator;
        /** Reference to dock/controlbar icon. **/
        private var _icon:DisplayObject;
        /** Currently active playlist item. **/
        private var _item:Object;
        /** XML connect and parse object. **/
        private var _loader:URLLoader;
        /* Reference to the JW Player. */
        private var _player:IPlayer;
        /** Reference to the captions renderer. **/
        private var _renderer:Renderer;
        /** Reference to the track selector. **/
        private var _selector:Selector;
        /** Current player state. **/
        private var _state:String;
        /** Map with style properties loaded by DFXP. **/
        private var _styles:Object;
        /** Currently active track. **/
        private var _track:Number;
        /** Current listing of tracks. **/
        private var _tracks:Array;


        /** Constructor; inits the parser, selector and renderer. **/
        public function Captions() {
            _cookie = SharedObject.getLocal('com.jeroenwijering','/');
            _loader = new URLLoader();
            _loader.addEventListener(Event.COMPLETE,_loaderHandler);
            _loader.addEventListener(IOErrorEvent.IO_ERROR,_errorHandler);
            _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_errorHandler);
            _tracks = new Array();
        };


        /** The controlbar is hiding. **/
        private function _barHideHandler(evt:Event):void {
            _easer.easeY(Math.round(_dimensions[1]*0.94));
        };


        /** The controlbar is showing. **/
        private function _barShowHandler(evt:Event):void {
            var yps:Number = evt['boundingRect'].y;
            if(yps > 0) {
                _easer.easeY(Math.round(yps * 0.94));
            }
        };


        /** Clicking the controlbar icon or dock button. **/
        private function _clickHandler(evt:MouseEvent):void {
            if(_tracks.length > 1) {
                _selector.alpha = 0;
                _renderer.visible = false;
                new Animator(_selector).fade(1,0.2);
                try { 
                    (_player.controls.display as Object).hide();
                    (_player.controls.dock as Object).hide();
                } catch (error:Error) {}
            } else if(_tracks.length == 1) {
                _config.state = !_config.state;
                _cookie.data['captions.state'] = _config.state;
                _cookie.flush();
                _redraw();
            }
        };


        /** The captions loader returns errors (file not found or security error. **/
        private function _errorHandler(event:ErrorEvent):void { 
            Logger.log(event.text);
        };


        /** Identifier string of this plugin in the JW Player. **/
        public function get id():String {
            return "captions";
        };


        /** Initialize as a JW Player 5 plugin. */
        public function initPlugin(player:IPlayer, config:PluginConfig):void {
            // Connect to the player API.
            _player = player;
            _player.addEventListener(PlaylistEvent.JWPLAYER_PLAYLIST_ITEM,_itemHandler);
            _player.addEventListener(MediaEvent.JWPLAYER_MEDIA_META,_metaHandler);
            _player.addEventListener(PlaylistEvent.JWPLAYER_PLAYLIST_LOADED,_playlistHandler);
            _player.addEventListener(PlayerStateEvent.JWPLAYER_PLAYER_STATE,_stateHandler);
            _player.addEventListener(MediaEvent.JWPLAYER_MEDIA_TIME,_timeHandler);
            // Load the configuration options and styling defaults.
            for (var entry:String in config) {
                _config[entry] = config[entry];
            }
            for (var rule:String in _defaults) {
                if (config[rule.toLowerCase()] != undefined) {
                    // Fix for colors, since the player automatically converts to HEX.
                    if(rule == 'color') {
                        _defaults['color'] = '#'+String(config['color']).substr(-6);
                    } else {
                        _defaults[rule] = config[rule.toLowerCase()];
                    }
                }
            }
            // Show button in either the dock or the controlbar.
            if (_player.config.dock) {
                _icon = _player.skin.getSkinElement('captions', 'dockIcon');
                if(!_icon) { _icon = new DockIcon(); }
                _button = _player.controls.dock.addButton(_icon, 'is on', _clickHandler);
            } else {
                _icon = _player.skin.getSkinElement('captions', 'controlbarButton');
                if(!_icon) { _icon = new ControlbarIcon(); }
                _player.controls.controlbar.addButton(_icon, 'captions', _clickHandler);
            }
            // Try connecting to controlbar show/hide events (5.7+) 
            try { 
                _player.controls.controlbar.addEventListener(ComponentEvent.JWPLAYER_COMPONENT_SHOW,_barShowHandler);
                _player.controls.controlbar.addEventListener(ComponentEvent.JWPLAYER_COMPONENT_HIDE,_barHideHandler);
            } catch (e:Error) {}
            // Place renderer and selector.
            _renderer = new Renderer(_defaults,_config.back);
            addChild(_renderer);
            _easer = new Animator(_renderer);
            _selector = new Selector(_selectorHandler);
            addChild(_selector);
            _selector.visible = false;
            _redraw();
        };


        /** Check playlist item for captions. **/
        private function _itemHandler(event:PlaylistEvent):void {
            _track = 0;
            _tracks = new Array();
            _renderer.setPosition(0);
            _item = _player.playlist.currentItem;
            if(_item['captions.files']) {
                // Load multiple captions
                var files:Array = _item['captions.files'].split(',');
                var found:Boolean = false;
                if(_item['captions.labels']) {
                    var labels:Array = _item['captions.labels'].split(',');
                }
                for(var i:Number = 0; i < files.length; i++) {
                    var entry:Object = { file: files[i] };
                    // Set label.
                    if(labels && labels[i]) {
                        entry.label = labels[i];
                    } else {
                        var slash:Number = files[i].lastIndexOf('/');
                        var dot:Number = files[i].indexOf('.');
                        entry.label = files[i].substr(slash+1,1).toUpperCase()+files[i].substr(slash+2,dot-slash-2);
                    }
                    if(_config.label == entry.label) {
                        _track = _tracks.length;
                        found = true;
                        _loader.load(new URLRequest(entry.file));
                    }
                    _tracks.push(entry);
                }
                if(found == false) { _config.state = false; }
                _populateSelector();
            } else if(_item['captions.file']) {
                // Load single caption
                _tracks.push({file:_item['captions.file']});
                _loader.load(new URLRequest(_item['captions.file']));
            }
            _redraw();
        };


        /** Parse and display external captions. **/
        private function _loaderHandler(event:Event):void {
            try {
                if(XML(event.target.data).localName().toString().toLowerCase() == DFXP.NAME) {
                    _tracks[_track].data = DFXP.parseCaptions(XML(event.target.data),_defaults);
                } else {
                    _tracks[_track].data = SRT.parseCaptions(String(event.target.data));
                }
            } catch (error:Error) {
                _tracks[_track].data = SRT.parseCaptions(String(event.target.data));
            }
            if (!_tracks[_track].data.length) {
                Logger.log('No captions entries found in file. Probably not a valid SRT or DFXP file?');
            } else {
                _renderer.setCaptions(_tracks[_track].data);
            }
            _redraw();
        };


        /** Check for captions in metadata. **/
        private function _metaHandler(event:MediaEvent):void {
            if(_state == PlayerState.IDLE) { return; }
            if(event.metadata.type == 'textdata') {
                if(event.metadata.trackid == _tracks[_track].id) {
                    _renderer.setCaptions(event.metadata.text.replace(/\n$/,''));
                }
            } else if (event.metadata.trackinfo && _tracks.length == 0) {
                _metaTracks(event.metadata.trackinfo);
            }
        };


        /** Parse track info from MP4 metadata. **/
        private function _metaTracks(info:Object):void {
            var found:Boolean = false;
            for(var i:Number = 0; i < info.length; i++) {
                if(info[i].sampledescription[0].sampletype == 'tx3g') {
                    if(_config.label == ISO639.label(info[i].language)) {
                        _track = _tracks.length;
                        found = true;
                    }
                    _tracks.push({
                        data: undefined,
                        file: undefined,
                        id: i,
                        label: ISO639.label(info[i].language)
                    });
                    // Override the ISO codes with labels
                    if(_item['captions.labels']) {
                        var index:Number = _tracks.length - 1;
                        _tracks[index].label = _item['captions.labels'].split(',')[index];
                    }
                }
            }
            if(_tracks.length > 1) {
                if(found == false) { _config.state = false; }
                _populateSelector();
            }
            _redraw();
        };


        /** Check for global captions upon playlsit load. **/
        private function _playlistHandler(event:PlaylistEvent):void {
            if(_player.playlist.getItemAt(0)['ova.hidden']) {
                return;
            }
            // Set the file, files, labels flashvar to the first playlistitem.
            if(_config.file) {
                _player.playlist.getItemAt(0)['captions.file'] = _config.file;
            }
            if(_config.files) {
                _player.playlist.getItemAt(0)['captions.files'] = _config.files;
            }
            if(_config.labels) {
                _player.playlist.getItemAt(0)['captions.labels'] = _config.labels;
            }
        };


        /** Populate the selector. **/
        private function _populateSelector():void { 
            var options:Array = new Array({label:'(Off)'});
            for(var j:Number=0; j<_tracks.length; j++) {
                options.push({label:_tracks[j].label});
            }
            if(_config.state) {
                _selector.populate(options,_track + 1);
            } else {
                _selector.populate(options,0);
            }
        };


        /** Show/hide the captions, update the button, save state in cookie. **/
        private function _redraw():void {
            if(!_tracks.length) {
                _icon.alpha = 0.2;
                if (_button) { 
                    _button.field.alpha = 0.3;
                    _button.field.text = 'not set';
                }
                _renderer.visible = false;
            } else if (!_config.state) {
                if (_button) {
                    if(_tracks.length > 1) { 
                        _button.field.text = '(off)';
                    } else { 
                        _button.field.text = 'is off';
                    }
                    _button.field.alpha = 1;
                    _icon.alpha = 0.5;
                } else {
                    _icon.alpha = 0.2;
                }
                _renderer.visible = false;
            } else {
                _icon.alpha = 0.5;
                if (_button) {
                    _button.field.alpha = 1;
                    if(_tracks.length > 1) { 
                        _button.field.text = _tracks[_track].label.toLowerCase();
                    } else { 
                        _button.field.text = 'is on';
                    }
                }
                if(_state == PlayerState.IDLE) {
                    _renderer.visible = false;
                } else {
                    _renderer.visible = true;
                }
            }
        };


        /** Resize the captions, relatively smaller as the screen grows */
        public function resize(width:Number, height:Number):void {
            _dimensions = new Array(width,height);
            _renderer.width = Math.round(400 * Math.pow(width/400,0.6));
            _renderer.x = Math.round(width/2 -_renderer.width/2);
            _renderer.scaleY = _renderer.scaleX;
            _renderer.y = Math.round(height * 0.94);
            _selector.resize(width,height);
        };


        /** Clicking the selector. **/
        private function _selectorHandler(index:Number):void {
            _selector.alpha = 1;
            new Animator(_selector).fade(0,0.2);
            try {
                (_player.controls.display as Object).show();
                (_player.controls.dock as Object).show();
            } catch (error:Error) {}
            // Set and cookie state/label
            if(index > 0) {
                _config.state = true;
                _track = index - 1;
                _config.label = _tracks[_track].label;
            } else {
                _config.state = false;
            }
            _cookie.data['captions.state'] = _config.state;
            _cookie.data['captions.label'] = _config.label;
            _cookie.flush();
            // Update UI
            if(_tracks[_track].file) {
                if(_tracks[_track].data) {
                    _renderer.setCaptions(_tracks[_track].data);
                } else { 
                    _loader.load(new URLRequest(_tracks[_track].file));
                }
            } else {
                _renderer.setCaptions('');
            }
            _redraw();
        };


        /** Hide the renderer when idle. **/
        private function _stateHandler(event:PlayerStateEvent):void {
            _state = event.newstate;
            _redraw();
        };


        /** Update the position in the video. **/
        private function _timeHandler(event:MediaEvent):void {
            _renderer.setPosition(event.position);
        };


    };


}
